package com.one.blocks.rbac.config;

import com.alicp.jetcache.anno.support.SpringConfigProvider;
import com.alicp.jetcache.autoconfigure.JetCacheAutoConfiguration;
import com.alicp.jetcache.autoconfigure.RedisAutoConfiguration;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.one.blocks.common.constant.CommonConstant;
import com.one.blocks.common.yaml.YamlPropertySourceFactory;
import com.one.blocks.mvc.valid.ValidatorProvider;
import com.one.blocks.mvc.valid.constraints.Unique;
import com.one.blocks.rbac.jetcache.JacksonKeyConvertor;
import com.one.blocks.rbac.jetcache.JacksonValueDecoder;
import com.one.blocks.rbac.jetcache.JacksonValueEncoder;
import com.one.blocks.rbac.valid.validator.UniqueValidatorForSysOrg;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import java.time.LocalDateTime;
import java.util.function.Function;

/**
 * @author linfuxin Created on 2022-02-14 14:12:16
 */
@MapperScan("com.one.blocks.rbac.mapper")
@ComponentScan(value = {
        "com.one.blocks.rbac.config", "com.one.blocks.rbac.controller",
        "com.one.blocks.rbac.manager", "com.one.blocks.rbac.security"
})
@EnableTransactionManagement
@AutoConfigureBefore({JetCacheAutoConfiguration.class, RedisAutoConfiguration.class})
@PropertySource(value = "classpath:application-rbac.yml", factory = YamlPropertySourceFactory.class)
public class RbacAutoConfig {

    @Bean
    @ConditionalOnMissingBean(name = "uniqueProvider")
    public ValidatorProvider uniqueProvider() {
        return constraintMapping -> constraintMapping.constraintDefinition(Unique.class)
                .validatedBy(UniqueValidatorForSysOrg.class)
                .includeExistingValidators(true);
    }

    @Bean
    @ConditionalOnMissingBean(SpringConfigProvider.class)
    public SpringConfigProvider springConfigProvider() {
        String myConvertor = "jackson";
        ObjectMapper objectMapper = buildObjectMapper();
        return new SpringConfigProvider() {

            @Override
            public Function<Object, Object> parseKeyConvertor(String convertor) {
                if (myConvertor.equalsIgnoreCase(convertor)) {
                    return new JacksonKeyConvertor(objectMapper);
                } else {
                    return super.parseKeyConvertor(convertor);
                }
            }

            @Override
            public Function<Object, byte[]> parseValueEncoder(String valueEncoder) {
                if (myConvertor.equalsIgnoreCase(valueEncoder)) {
                    return new JacksonValueEncoder(objectMapper);
                } else {
                    return super.parseValueEncoder(valueEncoder);
                }
            }

            @Override
            public Function<byte[], Object> parseValueDecoder(String valueDecoder) {
                if (myConvertor.equalsIgnoreCase(valueDecoder)) {
                    return new JacksonValueDecoder(objectMapper);
                } else {
                    return super.parseValueDecoder(valueDecoder);
                }
            }
        };
    }

//    @Bean
//    @ConditionalOnMissingBean(DataScopeInterceptor.class)
//    public DataScopeInterceptor dataScopeInterceptor() {
//        return new DataScopeInterceptor();
//    }

    @Bean
    @ConditionalOnMissingBean(RedisTemplate.class)
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        ObjectMapper objectMapper = buildObjectMapper();
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);
        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        return template;
    }

    private ObjectMapper buildObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        // 序列化不依赖 getter setter
        objectMapper.setVisibility(objectMapper.getSerializationConfig()
                .getDefaultVisibilityChecker()
                .withFieldVisibility(JsonAutoDetect.Visibility.ANY)
                .withGetterVisibility(JsonAutoDetect.Visibility.NONE)
                .withIsGetterVisibility(JsonAutoDetect.Visibility.NONE));
        // 不序列化transient
        objectMapper.enable(MapperFeature.PROPAGATE_TRANSIENT_MARKER);
        // 序列化时候遇到空对象不抛出异常
        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        // 不使用默认的dateTime进行序列化
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        // 不使用默认的dateTime进行序列化
        objectMapper.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);
        // 使用 JSR310 序列化时间类型，包含LocalDateTime和LocalDate
        JavaTimeModule module = new JavaTimeModule();
        module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(CommonConstant.DATE_COMMON_PATTERN));
        module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(CommonConstant.DATE_COMMON_PATTERN));
        objectMapper.registerModule(module);

        // 反序列化时候遇到不匹配的属性并不抛出异常
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        objectMapper.disable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE);
        // NON_FINAL反序列化可能存在漏洞，所以spring mvc默认的ObjectMapper不配置该属性
        // 序列化时在属性中添加@class，避免反序列化时抛异常 java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to xx
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
        return objectMapper;
    }
}